// MIT License (MIT)
// 
// Copyright (c) 2014 K Ercan Turkarslan
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// 

using System;
using System.Globalization;
using System.Linq;
using System.Text;
using Mios.MiosConnection;

namespace Mios
{
    public class BasicMiosEngine 
    {
        protected MiosController Controller { get; set; }

        #region Luup Requests
        // Http Luup Request methods
        // http://wiki.micasaverde.com/index.php/Luup_Requests
        //

        #region private static methods for formatting Luup Requests
        private static String LuupAction(String action)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            return String.Format("action={0}", action);
        }

        private static String LuupDevice(UInt32 deviceNum)
        {
            return String.Format("device={0}", deviceNum);
        }

        private static String LuupDeviceNum(UInt32 deviceNum)
        {
            return String.Format("DeviceNum={0}", deviceNum);
        }

        private static String LuupId(String id)
        {
            if (id == null)
                throw new ArgumentNullException("id");

            return String.Format("id={0}", id);
        }

        private static String LuupName(String name)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            return String.Format("name={0}", name);
        }

        private static String LuupOutputFormat(String outputFormat)
        {
            return outputFormat == null ? null : String.Format("output_format={0}", outputFormat);
        }

        private static String LuupParam(String name, String value)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            return (value == null) ? null : String.Format("{0}={1}", name, value);
        }

        private static String LuupRoom(UInt32 room)
        {
            return LuupRoom(room.ToString(CultureInfo.InvariantCulture));
        }

        private static String LuupRoom(String room)
        {
            if (room == null)
                throw new ArgumentNullException("room");

            return String.Format("room={0}", room);
        }

        private static String LuupScene(UInt32 sceneId)
        {
            return String.Format("scene={0}", sceneId);
        }

        private static String LuupServiceId(String serviceId)
        {
            if (String.IsNullOrEmpty(serviceId))
                throw new ArgumentNullException("serviceId");

            return String.Format("serviceId={0}", serviceId);
        }

        private static String LuupUdn(String udn)
        {
            if (udn == null)
                throw new ArgumentNullException("udn");

            return String.Format("UDN={0}", udn);
        }

        private static String LuupValue(String value)
        {
            if (String.IsNullOrEmpty(value))
                throw new ArgumentNullException("value");

            return String.Format("Value={0}", value);
        }

        private static String LuupVariable(String variableName)
        {
            if (String.IsNullOrEmpty(variableName))
                throw new ArgumentNullException("variableName");

            return String.Format("Variable={0}", variableName);
        }

        private static String Combine(params String[] parameters)
        {
            if ((parameters == null) || (!parameters.Any()))
                return "";

            var paramStr = new StringBuilder();

            var index = 0;
            foreach (var param in parameters.Where(param => param != null))
            {
                paramStr.AppendFormat("{0}{1}", (index++ > 0) ? "&" : "", param);
            }

            return paramStr.ToString();
        }
        #endregion

        public String Luup_UserData(String outputFormat = null, String loadTime = null, String dataVersion = null, String timeout = null, String minimumDelay = null)
        {
            // http://ip_address:3480/data_request?id=user_data&output_format=xml

            return
                Controller.GetString(Combine(
                    LuupId("user_data"),
                    LuupOutputFormat(outputFormat),
                    LuupParam("LoadTime", loadTime),
                    LuupParam("DataVersion", dataVersion),
                    LuupParam("timeout", timeout),
                    LuupParam("minimumdelay", minimumDelay)));
        }

        public String Luup_UserData2(String outputFormat = null, String loadTime = null, String dataVersion = null, String timeout = null, String minimumDelay = null)
        {
            return
                Controller.GetString(Combine(
                    LuupId("user_data2"),
                    LuupOutputFormat(outputFormat),
                    LuupParam("LoadTime", loadTime),
                    LuupParam("DataVersion", dataVersion),
                    LuupParam("timeout", timeout),
                    LuupParam("minimumdelay", minimumDelay)));
        }

        public String Luup_Status(String outputFormat = null, String loadTime = null, String dataVersion = null, String timeout = null, String minimumDelay = null)
        {
            // http://ip_address:3480/data_request?id=status&output_format=xml

            return
                Controller.GetString(Combine(
                    LuupId("status"),
                    LuupOutputFormat(outputFormat),
                    LuupParam("LoadTime", loadTime),
                    LuupParam("DataVersion", dataVersion),
                    LuupParam("timeout", timeout),
                    LuupParam("minimumdelay", minimumDelay)));
        }

        public String Luup_StatusWithUdn(String udn, String outputFormat)
        {
            // http://ip_address:3480/data_request?id=status&output_format=xml&UDN=uuid:4d494342-5342-5645-0002-000000000002

            return
                Controller.GetString(Combine(
                    LuupId("status"),
                    LuupOutputFormat(outputFormat),
                    LuupUdn(udn)));
        }

        public String Luup_Status(UInt32 deviceNum, String outputFormat = null, String loadTime = null, String dataVersion = null, String timeout = null, String minimumDelay = null)
        {
            // http://ip_address:3480/data_request?id=status&output_format=xml&DeviceNum=6

            return
                Controller.GetString(Combine(
                    LuupId("status"),
                    LuupOutputFormat(outputFormat),
                    LuupDeviceNum(deviceNum),
                    LuupParam("loadtime", loadTime),
                    LuupParam("dataversion", dataVersion),
                    LuupParam("timeout", timeout),
                    LuupParam("minimumdelay", minimumDelay)));
        }

        public String Luup_SData(String outputFormat = null, String loadTime = null, String dataVersion = null, String timeout = null, String minimumDelay = null)
        {
            // http://ip_address:3480/data_request?id=sdata 
            // http://ip_address:3480/data_request?id=sdata&output_format=xml 

            return
                Controller.GetString(Combine(
                    LuupId("sdata"),
                    LuupOutputFormat(outputFormat),
                    LuupParam("loadtime", loadTime),
                    LuupParam("dataversion", dataVersion),
                    LuupParam("timeout", timeout),
                    LuupParam("minimumdelay", minimumDelay)));
        }

        public String Luup_Actions(UInt32 deviceNum, String outputFormat = null)
        {
            return
                Controller.GetString(Combine(
                    LuupId("actions"),
                    LuupOutputFormat(outputFormat),
                    LuupDeviceNum(deviceNum)));
        }

        public String Luup_DeviceRename(UInt32 device, String newName, UInt32 newRoom)
        {
            // http://ip_address:3480/data_request?id=device&action=rename&device=5&name=Chandalier&room=3 
            return Luup_DeviceRename(device, newName, newRoom.ToString(CultureInfo.InvariantCulture));
        }

        public String Luup_DeviceRename(UInt32 device, String newName, String newRoom = null)
        {
            // http://ip_address:3480/data_request?id=device&action=rename&device=5&name=Chandalier&room=Garage 

            return
                Controller.GetString(Combine(
                    LuupId("device"),
                    LuupAction("rename"),
                    LuupDevice(device),
                    LuupName(newName),
                    LuupParam("room", newRoom)));
        }

        public String Luup_DeviceDelete(UInt32 device)
        {
            // http://ip_address:3480/data_request?id=device&action=delete&device=5

            return
                Controller.GetString(Combine(
                    LuupId("device"),
                    LuupAction("delete"),
                    LuupDevice(device)));
        }

        public String Luup_SceneRecord()
        {
            // http://ip_address:3480/data_request?id=scene&action=record

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("record")));
        }

        public String Luup_ScenePause(UInt32 seconds)
        {
            // http://ip_address:3480/data_request?id=scene&action=pause&seconds=y

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("pause"),
                    LuupParam("seconds", seconds.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_SceneStopRecord()
        {
            // http://ip_address:3480/data_request?id=scene&action=stoprecord

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("stoprecord")));
        }

        public String Luup_SceneListRecord()
        {
            // http://ip_address:3480/data_request?id=scene&action=listrecord

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("listrecord")));
        }

        public String Luup_SceneDeleteRecord(UInt32 number)
        {
            // http://ip_address:3480/data_request?id=scene&action=deleterecord&number=x

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("deleterecord"),
                    LuupParam("number", number.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_SceneSaveRecord(String name, String room)
        {
            // http://ip_address:3480/data_request?id=scene&action=saverecord&name=whatever&room=X

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("saverecord"),
                    LuupName(name),
                    LuupRoom(room)));
        }

        public String Luup_SceneRename(UInt32 sceneId, String name, String room)
        {
            // http://ip_address:3480/data_request?id=scene&action=rename&scene=5&name=Chandalier&room=Garage

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("rename"),
                    LuupScene(sceneId),
                    LuupName(name),
                    LuupRoom(room)));
        }

        public String Luup_SceneDelete(UInt32 sceneId)
        {
            // http://ip_address:3480/data_request?id=scene&action=delete&scene=5

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("delete"),
                    LuupScene(sceneId)));
        }

        public String Luup_SceneCreate(String json)
        {
            // http://ip_address:3480/data_request?id=scene&action=create&json=

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("create"),
                    LuupParam("json", json)));
        }

        public String Luup_SceneList(UInt32 sceneId)
        {
            // http://ip_address:3480/data_request?id=scene&action=list&scene=5

            return
                Controller.GetString(Combine(
                    LuupId("scene"),
                    LuupAction("list"),
                    LuupScene(sceneId)));
        }

        public String Luup_RoomCreate(String name)
        {
            // http://ip_address:3480/data_request?id=room&action=create&name=Kitchen

            return
                Controller.GetString(Combine(
                    LuupId("room"),
                    LuupAction("create"),
                    LuupName(name)));
        }

        public String Luup_RoomRename(UInt32 room, String name)
        {
            // http://ip_address:3480/data_request?id=room&action=rename&room=5&name=Garage

            return
                Controller.GetString(Combine(
                    LuupId("room"),
                    LuupAction("rename"),
                    LuupRoom(room),
                    LuupName(name)));
        }

        public String Luup_RoomDelete(UInt32 room)
        {
            // http://ip_address:3480/data_request?id=room&action=delete&room=5

            return
                Controller.GetString(Combine(
                    LuupId("room"),
                    LuupAction("create"),
                    LuupRoom(room)));
        }

        public String Luup_File(String filename)
        {
            // http://ip_address:3480/data_request?id=file&parameters=D_BinaryLight1.xml

            return
                Controller.GetString(Combine(
                    LuupId("file"),
                    LuupParam("parameters", filename)));
        }

        public String Luup_RunLua(String code)
        {
            return
                Controller.GetString(Combine(
                    LuupId("action"),
                    LuupServiceId("urn:micasaverde-com:serviceId:HomeAutomationGateway1"),
                    LuupAction("RunLua"),
                    LuupParam("Code", code)));
        }

        public String Luup_CallAction(UInt32 deviceNum, String serviceId, String action, params String[] parameters)
        {
            // http://ip_address:3480/data_request?id=action&output_format=xml&DeviceNum=6&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=1

            return
                Controller.GetString(Combine(
                    LuupId("action"),
                    LuupOutputFormat("xml"),
                    LuupDeviceNum(deviceNum),
                    LuupServiceId(serviceId),
                    LuupAction(action),
                    Combine(parameters)));
        }

        public String Luup_RunScene(UInt32 sceneId)
        {
            // http://ip_address:3480/data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=

            return
                Controller.GetString(Combine(
                    LuupId("action"),
                    LuupServiceId("urn:micasaverde-com:serviceId:HomeAutomationGateway1"),
                    LuupAction("RunScene"),
                    LuupParam("SceneNum", sceneId.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_InstallPlugin(UInt32 pluginNum)
        {
            // http://ip_address:3480/data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=CreatePlugin&PluginNum=

            return
                Controller.GetString(Combine(
                    LuupId("action"),
                    LuupServiceId("urn:micasaverde-com:serviceId:HomeAutomationGateway1"),
                    LuupAction("CreatePlugin"),
                    LuupParam("PluginNum", pluginNum.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_VariableSet(UInt32 deviceNum, String serviceId, String varName, String value)
        {
            // http://ip_address:3480/data_request?id=variableset&DeviceNum=6&serviceId=urn:micasaverde-com:serviceId:DoorLock1&Variable=Status&Value=1

            return
                Controller.GetString(Combine(
                    LuupId("variableset"),
                    LuupDeviceNum(deviceNum),
                    LuupServiceId(serviceId),
                    LuupVariable(varName),
                    LuupValue(value)));
        }

        public String Luup_VariableGet(UInt32 deviceNum, String serviceId, String varName)
        {
            // http://ip_address:3480/data_request?id=variableget&DeviceNum=6&serviceId=urn:micasaverde-com:serviceId:DoorLock1&Variable=Status

            return
                Controller.GetString(Combine(
                    LuupId("variableget"),
                    LuupDeviceNum(deviceNum),
                    LuupServiceId(serviceId),
                    LuupVariable(varName)));
        }

        public String Luup_Reload()
        {
            // http://ip_address:3480/data_request?id=reload

            return
                Controller.GetString(Combine(
                    LuupId("reload")));
        }

        public Boolean Luup_Alive()
        {
            return
                Controller.GetString(Combine(
                    LuupId("alive"))) == "OK";
        }

        public String Luup_FindDeviceByAltId(UInt32 altid)
        {
            // http://ip_address:3480/data_request?id=finddevice&devid=6

            return
                Controller.GetString(Combine(
                    LuupId("finddevice"),
                    LuupParam("devid", altid.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_FindDevice(UInt32 deviceNum)
        {
            // http://ip_address:3480/data_request?id=finddevice&devnum=6

            return
                Controller.GetString(Combine(
                    LuupId("finddevice"),
                    LuupParam("devnum", deviceNum.ToString(CultureInfo.InvariantCulture))));
        }

        public Boolean Luup_Resync()
        {
            return Controller.GetString(Combine(
                LuupId("resync"))) == "OK";
        }

        public String Luup_WGet(String url, UInt32 timeout, String username = null, String password = null)
        {
            return
                Controller.GetString(Combine(
                    LuupId("wget"),
                    LuupParam("url", url),
                    (username != null) ? LuupParam("username", username) : null,
                    (password != null) ? LuupParam("password", password) : null,
                    LuupParam("timeout", timeout.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_IPRequests()
        {
            // http://ip_address:3480/data_request?id=iprequests

            return Controller.GetString(LuupId("iprequests"));
        }

        public String Luup_IPRequests(Int32 timeout)
        {
            // http://ip_address:3480/data_request?id=iprequests&timeout=3600

            return
                Controller.GetString(Combine(
                    LuupId("iprequests"),
                    LuupParam("timeout", timeout.ToString(CultureInfo.InvariantCulture))));
        }

        public String Luup_LiveEnergyUsage()
        {
            return Controller.GetString(LuupId("live_energy_usage"));
        }

        public String Luup_JobStatus(UInt32 job, String plugin = null)
        {
            // http://ip_address:3480/data_request?id=jobstatus&job=13 
            // http://ip_address:3480/data_request?id=jobstatus&job=6&plugin=zwave 

            return
                Controller.GetString(Combine(
                    LuupId("jobstatus"),
                    LuupParam("job", job.ToString(CultureInfo.InvariantCulture)),
                    LuupParam("plugin", plugin)));
        }

        public String Luup_Invoke()
        {
            // http://ip_address:3480/data_request?id=invoke

            return
                Controller.GetString(LuupId("invoke"));
        }

        public String Luup_Invoke(UInt32 deviceNum)
        {
            // http://ip_address:3480/data_request?id=invoke&DeviceNum=6

            return
                Controller.GetString(Combine(
                    LuupId("invoke"),
                    LuupDeviceNum(deviceNum)));
        }

        public String Luup_Invoke(String udn)
        {
            // http://ip_address:3480/data_request?id=invoke&UDN=uuid:4d494342-5342-5645-0002-000000000002

            return
                Controller.GetString(Combine(
                    LuupId("invoke"),
                    LuupUdn(udn)));
        }

        public String Luup_UpdatePlugin(UInt32 pluginId)
        {
            // http://ip_address:3480/data_request?id=update_plugin&Plugin=Plugin_ID

            return
                Controller.GetString(Combine(
                    LuupId("update_plugin"),
                    LuupParam("Plugin", pluginId.ToString(CultureInfo.InvariantCulture))));
        }

        #endregion

        #region Constructor(s)

        public BasicMiosEngine(MiosController controller)
        {
            Controller = controller;

            if (Controller.IsConnected()) 
                 return;

            if (Controller.CanConnectLocally())
            {
                Controller.EstablishLocalConnection();
            }
            else
            {
                Controller.EstablishRemoteConnection();
            }
        }

        #endregion
    }
}